package com.eqxiu.ieditor.api.controller;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.eqxiu.ieditor.api.controller.doc.OrderApiDoc;
import com.eqxiu.ieditor.commons.web.MsgCode;
import com.eqxiu.ieditor.commons.web.ResultData;
import com.eqxiu.ieditor.interceptor.UserContext;
import com.eqxiu.ieditor.order.OrderStatusEnum;
import com.eqxiu.ieditor.order.dto.ItemDTO;
import com.eqxiu.ieditor.order.dto.OrderDTO;
import com.eqxiu.ieditor.order.dto.OrderTypeEnum;
import com.eqxiu.ieditor.order.model.OrderExample;
import com.eqxiu.ieditor.order.service.MallService;
import com.eqxiu.ieditor.order.service.OrderItemService;
import com.eqxiu.ieditor.order.service.OrderService;

import eqxiu.mall.product.service.api.AsyncOrderCallbackServiceApi;
import eqxiu.mall.product.service.api.ProductServiceApi;
import eqxiu.passport.sdk.dto.UserDTO;

@RestController
@RequestMapping("/api/m/order")
public class OrderApiController implements OrderApiDoc, BaseApiController {

    private static final int PAYWAY_WECHAT = 0;

    @Autowired
    private OrderService orderService;

    @Autowired
    private OrderItemService itemService;

    @Autowired
    private ProductServiceApi productService;

    @Autowired
    private AsyncOrderCallbackServiceApi callbackServiceApi;

    @Value("${payment.appid}")
    private String paymentAppId;

    @Value("${payment.notify}")
    private String paymentNotify;

    @Value("${payment.notifyCharge}")
    private String paymentNotifyCharge;

    @Value("${payment.notifyTailer}")
    private String paymentNotifyTailer;

    @Autowired
    private MallService mallService;

    private final Logger logger = LoggerFactory.getLogger(OrderApiController.class);

    @Autowired
    private ObjectMapper mapper;
    // 样例模板
    private final int SCENE_ATTR_GROUP_ID = 2;
    // 实时数据价格
    private final int SCENE_DATE_IN_TIME = 5;
    // 实时数据和推送组合价格
    private final int SCENE_DATE_IN_TIME_PUSH = 8;

    @PostMapping(value = "/create")
    public ResultData createOrder(@RequestBody OrderDTO orderDTO, HttpServletRequest req)
        throws JsonGenerationException, JsonMappingException, IOException {
        String code = orderDTO.getProductCode();
        Integer productId = orderDTO.getProductId();
        String notifyUrl = orderDTO.getNotifyUrl();
        boolean existCode = !StringUtils.isEmpty(code);

        if (!existCode && productId == null || orderDTO.getPayType() == null) {
            return fail().setCode(MsgCode.CONSTRAINT_VIOLATION)
                .setMsg("create order failed, code or id or payType is null");
        }

        Map<String, Object> prod = null;

        if (existCode) {
            prod = productService.getProductLiteByCodeAndAttrGroupId(code, SCENE_ATTR_GROUP_ID);// productService.getListBySourceId("")
        } else {
            prod = productService.getProductLite(productId);
        }

        if (prod == null) {
            return fail().setCode(MsgCode.DATA_NOT_EXIST).setMsg("product not exist");
        }

        String title = prod.get("title").toString();
        int price = Integer.parseInt(prod.get("price").toString());
        int prodId = Integer.parseInt(prod.get("id").toString());
        code = prod.get("code").toString();
        UserDTO currentUser = UserContext.getUser();
        orderDTO.setCreateUser(currentUser.getId());
        orderDTO.setProductId(prodId);
        orderDTO.setName(title);

        Map<String, Object> map = new TreeMap<String, Object>();

        Integer orderType = orderDTO.getPayType();
        if (orderType == OrderTypeEnum.Charge.getState()) {// 充值订单
            Integer goodsId = 25;
            Integer amount = 1;
            map.put("goodsId", goodsId);
            map.put("amount", amount);
            map.put("price", price);
            // 如果有优惠券且价格>0
            if (Objects.nonNull(orderDTO.getCouponId()) && orderDTO.getTotalFee() > 0) {
                map.put("price", orderDTO.getTotalFee());
            } else {
                orderDTO.setTotalFee(price);
            }
            map.put("remark", orderDTO.getRemark());
            map.put("payway", PAYWAY_WECHAT);
            map.put("notifyUrl", notifyUrl == null ? paymentNotify : notifyUrl);
        } else if (orderType == OrderTypeEnum.COST.getState()) {// 消耗
            orderDTO.setTotalFee(price);
            map.put("orderAmount", price);
            Map<String, Object> props = new LinkedHashMap<>();
            props.put("productId", prodId);
            props.put("productName", title);
            props.put("code", code);
            map.put("orderRemark", orderDTO.getRemark());
            map.put("properties", mapper.writeValueAsString(props));
            map.put("orderType", "PAYXB");
            map.put("notifyUrl", notifyUrl == null ? paymentNotify : notifyUrl);
        }
        orderService.createOrder(orderDTO);
        map.put("orderTradeId", orderDTO.getId());
        map.put("orderAppId", paymentAppId);
        logger.info("sign param {}", map.toString());
        String sign = orderService.signParam(map);
        map.put("sign", sign);

        return success().setObj(orderDTO).setMap(map);
    }

    @PostMapping(value = "/create2")
    public ResultData createOrder2(@RequestBody OrderDTO orderDTO, HttpServletRequest req)
        throws JsonGenerationException, JsonMappingException, IOException {
        Integer totalFee = orderDTO.getTotalFee();

        if (totalFee <= 0) {
            return success().setCode(MsgCode.CONSTRAINT_VIOLATION).setMsg("total fee invalid");
        }
        UserDTO currentUser = UserContext.getUser();
        orderDTO.setCreateUser(currentUser.getId());
        List<ItemDTO> items = orderDTO.getItems();
        if (items != null) {
            // hard code totalFee
            if (items.size() == 1 && totalFee != SCENE_DATE_IN_TIME) {
                return success().setCode(MsgCode.CONSTRAINT_VIOLATION).setMsg("total fee invalid");
            }
            if (items.size() == 2 && totalFee != SCENE_DATE_IN_TIME_PUSH) {
                return success().setCode(MsgCode.CONSTRAINT_VIOLATION).setMsg("total fee invalid");
            }
            if (items.size() >= 3) {
                return success().setCode(MsgCode.CONSTRAINT_VIOLATION).setMsg("items invalid");
            }
        }

        orderService.createOrder(orderDTO);

        Map<String, Object> map = new TreeMap<String, Object>();

        map.put("orderAmount", orderDTO.getTotalFee());
        map.put("orderAppId", paymentAppId);
        map.put("orderTradeId", orderDTO.getId());
        Map<String, Object> props = new LinkedHashMap<>();
        props.put("productName", orderDTO.getName());
        if (orderDTO.getProductId() != null)
            props.put("productId", orderDTO.getProductId());
        if (orderDTO.getProductCode() != null)
            props.put("code", orderDTO.getProductCode());
        map.put("orderRemark", orderDTO.getRemark());
        map.put("properties", mapper.writeValueAsString(props));
        map.put("orderType", "PAYXB");
        map.put("notifyUrl", paymentNotifyTailer);

        logger.info("sign param {}", map.toString());
        String sign = orderService.signParam(map);
        map.put("sign", sign);

        return success().setObj(orderDTO).setMap(map);
    }

    @GetMapping(value = "/{orderId}")
    public ResultData queryOrder(@PathVariable("orderId") Long orderId) throws Exception {

        UserDTO currentUser = UserContext.getUser();
        OrderExample example = new OrderExample();
        example.createCriteria().andIdEqualTo(orderId).andCreateUserEqualTo(currentUser.getId());
        Optional<List<OrderDTO>> order = orderService.selectByExample(example);
        if (!order.isPresent()) {
            return fail().setCode(MsgCode.DATA_NOT_EXIST).setMsg("order not exist");
        }

        OrderDTO dto = order.get().get(0);

        if (dto.getAsyncId() != null) {
            Map<String, Object> result = callbackServiceApi.isOrderCallbackSuccessById(dto.getAsyncId());
            return success().setObj(dto).setMap(result);
        }

        return success().setObj(dto);
    }

    @RequestMapping(value = "/try-use", method = {RequestMethod.GET, RequestMethod.POST})
    public ResultData useProduct(@RequestParam(required = true) String code,
        @RequestParam(required = false, defaultValue = "4") Integer source) {
        UserDTO currentUser = UserContext.getUser();
        OrderExample example = new OrderExample();
        example.createCriteria().andCodeEqualTo(code).andCreateUserEqualTo(currentUser.getId())
            .andStatusEqualTo(OrderStatusEnum.PAID.status()).andPayTypeEqualTo(OrderTypeEnum.COST.getState());
        Optional<List<OrderDTO>> order = orderService.selectByExample(example);

        if (!order.isPresent()) {
            logger.error("try use product failed , order not exist. code={},userid={}", code, currentUser.getId());
            return fail().setCode(MsgCode.DATA_NOT_EXIST).setMsg("order not exist");
        }
        OrderDTO dto = order.get().get(0);
        String channel = dto.getChannel();
        // 做兼容
        source = mallService.determineCallbackSource(channel, source);

        Map<String, Object> result = mallService.createCallback("PAYXB", dto.getTotalFee() + "", dto.getProductId(),
            currentUser.getId(), dto.getMallOrderId(), true, source);

        if (result == null) {
            logger.error("create mall callback failed product id {}, userid {}, mall order id : {}", dto.getProductId(),
                currentUser.getId(), dto.getMallOrderId());
        }

        Integer asyncId = (Integer)result.get("id");

        dto.setAsyncId(asyncId);
        orderService.finishOrder(dto);
        return success().setObj(dto);
    }

    @RequestMapping(value = "/exist", method = {RequestMethod.GET, RequestMethod.POST})
    public ResultData hasOrder(@RequestParam String code, @RequestParam Integer itemType) {
        UserDTO currentUser = UserContext.getUser();
        return success().setObj(itemService.countByItemType(currentUser.getId(), code, itemType) > 0);
    }

}
